﻿using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace umbraco.DataServices.Query
{
    public class UmbracoQueryProvider : IQueryProvider {

        /// <summary>The underlying query provider (the LINQ to Objects provider) determined from the source query.</summary>
        private readonly IQueryProvider _underlyingQueryProvider;

        private UmbracoQueryProvider(IQueryProvider underlyingQueryProvider) {
            _underlyingQueryProvider = underlyingQueryProvider;
        }

        /// <summary>
        /// Send To Generic CreateQuery
        /// </summary>
        public IQueryable CreateQuery(Expression expression) {
            var et = TypeSystem.GetIEnumerableElementType(expression.Type);
            var qt = typeof(UmbracoQuery<>).MakeGenericType(et);
            var args = new object[] { this, expression };
            var ci = qt.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(UmbracoQueryProvider), typeof(Expression) }, null);
            return (IQueryable)ci.Invoke(args);
        }

        public IQueryable<TElement> CreateQuery<TElement>(Expression expression) {
            return new UmbracoQuery<TElement>(this, expression);
        }

        public object Execute(Expression expression) {
            expression = ProcessExpression(expression);
            return _underlyingQueryProvider.Execute(expression);
        }

        public TResult Execute<TResult>(Expression expression) {
            expression = ProcessExpression(expression);
            return _underlyingQueryProvider.Execute<TResult>(expression);
        }

        /// <summary>
        /// Writes The Expression Tree, For Querying The Property Dictionary
        /// </summary>
        internal IEnumerator<TElement> ExecuteQuery<TElement>(Expression expression) {
            expression = ProcessExpression(expression);
            return _underlyingQueryProvider.CreateQuery<TElement>(expression).GetEnumerator();
        }

        private static Expression ProcessExpression(Expression expression) {
            return PropertyTranslatingVisitor.TranslateExpression(expression);
        }

        public static IQueryable ElevateQuery(IQueryable underlyingQuery) {
            var provider = new UmbracoQueryProvider(underlyingQuery.Provider);
            return provider.CreateQuery(underlyingQuery.Expression);
        }
    }
}
